package bus

import chisel3._
import chisel3.util._
import utils._

object CBUSToAHBLState extends ChiselEnum {
  val IDLE, WAITREQ1, WAITREQ21, WAITREQ22, RESPERROR = Value
}

class CBus64ToAHBL64(conf : AhbConfig) extends Module {
    val io = IO(new Bundle{
        val in  = Flipped(new CBus64Bundle(64, 32))
        val out = new AhbBundle(conf)
        val clk2en = Input(Bool())
    })
    val in = io.in
    val out = io.out
    val clk2en = io.clk2en

    import CBUSToAHBLState._
    val fsm_ns = WireInit(IDLE)
    val fsm_cs = RegNextWithEnableA(reset, clk2en, fsm_ns, IDLE)

    val needSplit = in.req.bits.needSplit
    val s1_needSplit = RegNextWithEnableA(reset, clk2en, fsm_cs === WAITREQ21 && fsm_ns === RESPERROR, false.B)
    val hready = out.hready && clk2en
    val herr = out.hresp.orR

    out.lock() := false.B

    val hbusreq = out.busreq()
    if (!conf.busReqHold) (hbusreq := in.req.valid | fsm_ns =/= IDLE)

    val s1_wdata = RegNextWithEnableA(reset, hready, in.req.bits.wdata, 0.U.asTypeOf(in.req.bits.wdata))

    switch(fsm_cs) {
        is(IDLE) {
            fsm_ns := Mux(in.req.valid & hready & !herr, 
                        Mux(needSplit, WAITREQ21, WAITREQ1), 
                        Mux(!hready & herr, RESPERROR, IDLE))
        }
        is(WAITREQ1) {
            fsm_ns := Mux(hready & !herr, 
                        Mux(in.req.valid,
                          Mux(needSplit, WAITREQ21, WAITREQ1),
                          IDLE), 
                        Mux(!hready & herr, RESPERROR, WAITREQ1))
        }
        is(WAITREQ21) {
            fsm_ns := Mux(hready & !herr, 
                        WAITREQ22, 
                        Mux(!hready & herr, RESPERROR, WAITREQ21))
        }
        is(WAITREQ22) {
            fsm_ns := Mux(hready & !herr,
                        Mux(in.req.valid,
                          Mux(needSplit, WAITREQ21, WAITREQ1),
                          IDLE), 
                        Mux(!hready & herr, RESPERROR, WAITREQ22))
        }
        is(RESPERROR) {
            fsm_ns := Mux(hready & herr, IDLE, RESPERROR)
        }
   }

    val addr1Offset = MuxCase(0.U(2.W), Seq(
        (in.req.bits.be(0).asBool) -> 0.U(2.W),
        (in.req.bits.be(1).asBool) -> 1.U(2.W),
        (in.req.bits.be(2).asBool) -> 2.U(2.W),
        (in.req.bits.be(3).asBool) -> 3.U(2.W)
    ))

    val addr1 = Cat(in.req.bits.addr(31,3), in.req.bits.addr(2) & !in.req.bits.mr, addr1Offset)
    val addr2 = Cat(in.req.bits.addr(31,2), 2.U(2.W))

    val size1 = MuxLookup(in.req.bits.be, 0.U(3.W))(Seq(
        // "b0110".U -> 0.U(3.W),
        "b0111".U -> 1.U(3.W),
        // "b1110".U -> 0.U(3.W),
        "b1111".U -> 2.U(3.W),
        "b0011".U -> 1.U(3.W),
        "b1100".U -> 1.U(3.W)
    ))
    val size2 = Mux(in.req.bits.be(3), 1.U(3.W), 0.U(3.W))

    in.req.ready := hready & (fsm_ns =/= WAITREQ21 & fsm_cs =/= RESPERROR | fsm_cs === RESPERROR & s1_needSplit)
    in.resp.valid := hready & (fsm_cs === WAITREQ1 | fsm_cs === WAITREQ22 | fsm_cs === RESPERROR)
    in.resp.bits.err := in.resp.valid & out.hresp.orR

    out.haddr := Mux(fsm_cs === WAITREQ21, addr2, addr1)
    out.htrans := Mux((in.req.valid & fsm_cs =/= RESPERROR) | fsm_cs === WAITREQ21, AHBParameters.TRANS_NONSEQ, AHBParameters.TRANS_IDLE)
    out.hwrite := in.req.bits.we
    out.hsize := Mux(fsm_cs === WAITREQ21, size2, Mux(in.req.bits.mr, 3.U(3.W), size1))

    out.hburst := AHBParameters.BURST_SINGLE
    out.hwdata := s1_wdata

    out.hprot := DontCare

    val in_resp_mask1 = RegNextWithEnableA(reset, hready & fsm_cs === WAITREQ21, Cat(0.U(16.W), Fill(8, in.req.bits.be(1)), Fill(8, in.req.bits.be(0))))
    val in_resp_mask2 = RegNextWithEnableA(reset, hready & fsm_cs === WAITREQ21, Cat(Fill(8, in.req.bits.be(3)), Fill(8, in.req.bits.be(2)), 0.U(16.W)))

    val resp_rdata1 = RegNextWithEnableA(reset, hready & fsm_cs === WAITREQ21, out.hrdata)
    val resp_rdata2 = out.hrdata
    val resp_rdata12 = Cat(in_resp_mask1, in_resp_mask1) & resp_rdata1 | Cat(in_resp_mask2, in_resp_mask2) & resp_rdata2

    in.resp.bits.rdata := Mux(fsm_cs === WAITREQ22, resp_rdata12, resp_rdata2)
    in.resp.bits.err := fsm_cs === RESPERROR

}

